// 通过苹果笔记本电脑的构造理解建造者模式

#include <iostream>
#include <memory>

// 抽象产品类
class Computer
{
public:
	Computer() {}
	void setBoard(const std::string &board)
	{
		_board = board;
	}

	void setDisplay(const std::string &display)
	{
		_display = display;
	}

	void showParamaters()
	{
		std::string computer = "Computer:{\n";
        computer += "\tboard=" + _board + ",\n";
        computer += "\tdisplay=" + _display + ",\n";
        computer += "\tOs=" + _os + ",\n";
        computer += "}\n";
		std::cout << computer << std::endl;
	}

	virtual void setOs() = 0;
	// 构造电脑所需要的零部件
protected: // 用 private，public 继承看不见，所以写 protected
	std::string _board;
	std::string _display;
	std::string _os;
};

// 具体产品类
class MacBook : public Computer
{
public:
	void setOs() override
	{
		_os = "MAC OS x12";
	}
};

// 抽象 Builder 类
class Builder
{
public:
	virtual void buildBoard(const std::string &board) = 0;
	virtual void buildDisplay(const std::string &display) = 0;
	virtual void buildOs() = 0;
	virtual std::shared_ptr<Computer> build() = 0;
};

// 具体产品 Builder 类
class MacBookBuilder : public Builder
{
public:
	MacBookBuilder() : _computer(new MacBook()) {}
	void buildBoard(const std::string &board)
	{
		_computer->setBoard(board);
	}
	
	void buildDisplay(const std::string &display)
	{
		_computer->setDisplay(display);
	}

	void buildOs()
	{
		_computer->setOs();
	}

	std::shared_ptr<Computer> build()
	{
		return _computer;
	}

private:
	std::shared_ptr<Computer> _computer;
};

// 指挥者 Director 类
class Director
{
public:
	Director(Builder* builder): _builder(builder) {}
	void construct(const std::string& board, const std::string& display)
	{
		_builder->buildBoard(board);
		_builder->buildDisplay(display);
		_builder->buildOs();
	}
private:
	std::shared_ptr<Builder> _builder;	
};

int main()
{
	Builder* builder = new MacBookBuilder(); // 构造一个具体产品类
	std::unique_ptr<Director> director(new Director(builder)); // 构造一个指挥者类
	director->construct("华硕主板", "三星显示器");
	std::shared_ptr<Computer> computer = builder->build();
	computer->showParamaters();
	return 0;
}